import MapTool from './MapTool';

/**
 * BMapLib是百度地图开源库：https://developer.baidu.com/map/library.htm
 * BMapLib.GeoUtils是几何计算工具
 * BMapLib.AreaRestriction是区域限制工具
 */
const {BMap, BMapLib} = window;

/**
 * 基于百度地图开源库封装的工具类
 */
const MapToolLib = {
  /**
   * 两条线段是否相交
   * 思路:判断每一条线段的两个端点是否都在另一条线段的两侧, 是则相交, 否则不相交。
   * 参考：https://www.jb51.net/article/90104.htm
   * @param lineA 线段A，格式：[Point,Point]
   * @param lineB 线段B，格式：[Point,Point]
   */
  isLineIntersect(lineA, lineB) {
    if (!lineA || !lineB) {
      return false;
    }

    let [a, b] = lineA;
    let [c, d] = lineB;
    //如果线段的两点重合时，当做他们不相交
    if (a.equals(b) || c.equals(d)) {
      return false;
    }

    //三角形abc的向量面积
    const abc = this.getArea(a, b, c);
    const abd = this.getArea(a, b, d);
    //abc * abd大于0，则c、d在ab的同边
    if (abc * abd > 0) {
      return false;
    }

    const cda = this.getArea(c, d, a);
    //通过已知的三个面积加减得出cdb的面积
    const cdb = cda + abc - abd;
    //cda * cdb大于0，则a、b在cd的同边
    if (cda * cdb > 0) {
      return false;
    }

    //lineA的斜率
    let t = cda / (abd - abc);
    let lng = a.lng + t * (b.lng - a.lng);
    let lat = a.lat + t * (b.lat - a.lat);
    return MapTool.getPoint({lng, lat});
  },

  /**
   * 两条线段是否重合
   * @param lineA 线段A，格式：[Point,Point]
   * @param lineB 线段B，格式：[Point,Point]
   */
  isLineCoincide(lineA, lineB) {
    if (!lineA || !lineB) {
      return false;
    }

    //百度地图折线
    let lA = new BMap.Polyline(lineA);
    //判断lineB上的顶点是否都在lineA上
    let isCoincideA = lineB.every((item) => BMapLib.GeoUtils.isPointOnPolyline(item, lA));
    console.log('isCoincideA', isCoincideA);

    let lB = new BMap.Polyline(lineB);
    //判断lineA上的顶点是否都在lineB上
    let isCoincideB = lineA.every((item) => BMapLib.GeoUtils.isPointOnPolyline(item, lB));
    console.log('isCoincideB', isCoincideB);
    return isCoincideA || isCoincideB;
  },

  /**
   * 判断多边形是否一条直线
   * @param ply 多边形，格式：Polygon
   */
  isPolygonAsLine(ply) {
    if (!ply) {
      return false;
    }

    let points = ply.getPath();
    if (points.length < 3) {
      return true;
    }

    //百度地图折线
    let line = new BMap.Polyline([points[0], points[points.length - 1]]);
    return points.every((item) => BMapLib.GeoUtils.isPointOnPolyline(item, line));
  },

  /**
   * 判断多边形A是否被多边形B包含
   * 思路：判断A的所有顶点是否在B中
   * @param plyA 多边形A，格式：Polygon
   * @param plyB 多边形B，格式：Polygon
   */
  isPolygonInPolygon(plyA, plyB) {
    if (!plyA || !plyB) {
      return false;
    }

    //获取多边形A的顶点数组
    let array = plyA.getPath();
    //注意：BMapLib.GeoUtils.isPointInPolygon()会校验参数的类型
    return array.every((p) => BMapLib.GeoUtils.isPointInPolygon(p, plyB));
  },

  /**
   * 判断两多边形是否相交
   * 思路：先判断两个多边形是否相互包含，然后判断是否有边界相交
   * @param plyA 多边形A，格式：Polygon
   * @param plyB 多边形B，格式：Polygon
   */
  isPolygonIntersect(plyA, plyB) {
    if (!plyA || !plyB) {
      return false;
    }

    //判断是否包含
    if (this.isPolygonInPolygon(plyA, plyB) || this.isPolygonInPolygon(plyB, plyA)) {
      return true;
    }

    let arrayA = plyA.getPath();
    let arrayB = plyB.getPath();
    //判断是否边界相交
    for (let i = 0; i < arrayA.length; i++) {
      let lineA = [arrayA[i], arrayA[i === arrayA.length - 1 ? 0 : i + 1]];
      for (let j = 0; j < arrayB.length; j++) {
        let lineB = [arrayB[j], arrayB[j === arrayB.length - 1 ? 0 : j + 1]];
        if (this.isLineIntersect(lineA, lineB)) {
          return true;
        }
      }
    }
    return false;
  },

  /**
   * 获取两多边形交点
   * 思路：先判断两个多边形是否相互包含，是则退出，否则获取边界交点
   * @param plyA 多边形A，格式：Polygon
   * @param plyB 多边形B，格式：Polygon
   */
  getPolygonCross(plyA, plyB) {
    if (!plyA || !plyB) {
      return;
    }

    //判断是否包含
    if (this.isPolygonInPolygon(plyA, plyB) || this.isPolygonInPolygon(plyB, plyA)) {
      return;
    }

    let arrayA = plyA.getPath();
    let arrayB = plyB.getPath();
    let crossArray = []; //交点集合
    //判断是否有边界相交
    for (let i = 0; i < arrayA.length; i++) {
      let i1 = i === arrayA.length - 1 ? 0 : i + 1; //下一个点
      let lineA = [arrayA[i], arrayA[i1]];
      for (let j = 0; j < arrayB.length; j++) {
        let j1 = j === arrayB.length - 1 ? 0 : j + 1; //下一个点
        let lineB = [arrayB[j], arrayB[j1]];
        let cross = this.isLineIntersect(lineA, lineB);
        cross && crossArray.push({point: cross, a0: i, a1: i1, b0: j, b1: j1});
      }
    }
    return crossArray;
  },

  /**
   * 两多边形自动贴边
   * 注意：目前支持多形交点个数为2的情形。
   * 思路：先获取两个多边形的交点，然后计算A中与B的重合点，然后去掉B中与A的重合点，然后拼接剩下的点
   * @param plyA 多边形A，格式：Polygon
   * @param plyB 多边形B，格式：Polygon
   */
  welPolygon(plyA, plyB) {
    if (!plyA || !plyB) {
      return;
    }

    let crossArray = this.getPolygonCross(plyA, plyB);
    if (!crossArray || crossArray.length !== 2) {
      return;
    }

    let arrayA = plyA.getPath();
    let arrayB = plyB.getPath();
    let [p0, p1] = crossArray;

    //需要插入plyB的点数组
    let arrays = [];
    //如果p0.a0在plyB中，则p1.a1到p0.a0的点都在plyB中
    if (BMapLib.GeoUtils.isPointInPolygon(arrayA[p0.a0], plyB)) {
      //plyA在plyB上面的顶点
      arrays = this.slice(arrayA, p1.a1, p0.a0);
      //插入plyA与plyB的交点
      arrays.unshift(p1.point);
      arrays.push(p0.point);
      //保证arrays是正向
      arrays.reverse();
    } else {
      //如果p0.a1在plyB中，则p0.a1到p1.a0的点都在plyB中
      if (BMapLib.GeoUtils.isPointInPolygon(arrayA[p0.a1], plyB)) {
        //plyA在plyB上面的顶点
        arrays = this.slice(arrayA, p0.a1, p1.a0);
      }
      //插入plyA与plyB的交点
      arrays.unshift(p0.point);
      arrays.push(p1.point);
    }

    //plyB中需要保留的点数组
    let arrays2 = [];
    //如果p0.b0在plyA中，则p0.b1到p1.b0的点都在plyA外
    if (BMapLib.GeoUtils.isPointInPolygon(arrayB[p0.b0], plyA)) {
      //plyB在plyA上面的顶点
      arrays2 = this.slice(arrayB, p0.b1, p1.b0);
      //arrays2是正向，需要将arrays逆向，才能正常连接
      arrays.reverse();
      arrayB = arrays2.concat(arrays);
      //如果p0.b1在plyA中，则p1.b1到p0.b0的点都在plyA外
    } else if (BMapLib.GeoUtils.isPointInPolygon(arrayB[p0.b1], plyA)) {
      //plyA在plyB上面的顶点
      arrays2 = this.slice(arrayB, p1.b1, p0.b0);
      arrayB = arrays2.concat(arrays);
    } else {
      //百度地图
      let map = plyA.getMap();
      //计算两交点所在有向线段顺时针方向旋转的角度
      let deg1 = MapTool.angleCalculation(arrays[0], arrays[arrays.length - 1], map);
      //计算包含两交点的两顶点所在有向线段顺时针方向旋转的角度
      let deg2 = MapTool.angleCalculation(arrayB[p0.b0], arrayB[p0.b1], map);
      //如果deg1与deg2相差大于1，代表deg1与deg2不等，说明arrays与arrayB异向，需要颠倒arrays的顺序
      if (Math.abs(deg1 - deg2) > 1) {
        arrays.reverse();
      }
      arrayB.splice(p0.b1, 0, ...arrays);
    }

    plyB.setPath(arrayB);
    return arrayB;
  },

  /**
   * 限制地图浏览区域
   * @param param
   * {
   *   map,//百度地图组件
   *   bounds,//区域，格式：Bounds|Polygon
   * }
   */
  setAreaBounds(param = {}) {
    let {map, bounds} = param;

    //如果bounds是多边形，则需要获取Bounds
    if (bounds instanceof BMap.Polygon) {
      map = bounds.getMap();
      bounds = bounds.getBounds();
    }

    BMapLib.AreaRestriction.setBounds(map, bounds);
  },
  /**
   * 计算三角形的向量面积：带方向
   * @param x 顶点，格式：Point
   * @param y 顶点，格式：Point
   * @param z 顶点，格式：Point
   */
  getArea(x, y, z) {
    let result = (x.lng - z.lng) * (y.lat - z.lat) - (x.lat - z.lat) * (y.lng - z.lng);
    return result / 2;
  },
  /**
   * 截取数组中的元素
   * @param arr 数组
   * @param start 开始元素的下标，格式：number
   * @param end 结束元素的下标，包含该下标，格式：number。如果end小于start，则回头遍历。
   */
  slice(arr, start, end) {
    if (!arr || !Array.isArray(arr)) {
      return;
    }

    if (start <= end) {
      return arr.slice(start, end + 1);
    } else {
      let result = arr.slice(start, arr.length);
      //从头开始截取
      let result2 = arr.slice(0, end + 1);
      return result.concat(result2);
    }
  }
};

export default MapToolLib;
